package org.lex.depends.core;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;

import org.lex.depends.core.resource.IResource;
import org.lex.depends.core.resource.ResourceManager;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.EmptyVisitor;
import org.objectweb.asm.tree.ClassNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Depends {
	private Logger log = LoggerFactory.getLogger(Depends.class);

	Queue<String> queue = new LinkedList<String>();

	public Collection<String> depends(String className) {
		Set<String> visited = new TreeSet<String>();
		queue.clear();
		queue.offer(className);
		while (false == queue.isEmpty()) {
			String top = queue.poll();
			if (visited.contains(top)) {
				log.debug(top + " has been visited");
				continue;
			}
			if (top.startsWith("java")) {
				log.debug(top + " is system class");
				continue;
			}
			log.debug("visit: " + top);
			try {
				IResource res = ResourceManager.getInstance().getResource(top);
				if (null == res) {
					log.error("-----" + top + " NOT FOUND!!!!");
					continue;
				}
				InputStream is = res.getInputStream();
				ClassReader cr = new ClassReader(is);
				ClassNode root = new ClassNode();
				cr.accept(root, ClassReader.SKIP_DEBUG);
				root.accept(new PrintClass());
				visited.add(top);
			} catch (Exception e) {
				log.error("name=" + top, e);
			}
		}
		Set<File> locs = new HashSet<File>();
		System.out.println("---dependent classes=" + visited.size());
		for (String s : visited) {
			System.out.println(s);
			File file = ResourceManager.getInstance().getLocation(s);
			locs.add(file);
		}
		System.out.println("---dependent files=" + locs.size());
		File[] files = locs.toArray(new File[0]);
		Arrays.sort(files, new Comparator<File>() {
			@Override
			public int compare(File o1, File o2) {
				boolean dir1 = o1.isDirectory();
				boolean dir2 = o2.isDirectory();
				if (dir1 != dir2) {
					return true == dir1 ? -1 : 1;
				} else {
					return o1.compareTo(o1);
				}
			}
		});
		for (File f : files) {
			System.out.println(f);
		}
		return visited;
	}

	class PrintClass extends EmptyVisitor {
		@Override
		public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
			log.trace(("access=" + access + ", name=" + name + ", desc=" + desc + ", sign=" + signature));
			return new PrintMethod();
		}
	}

	class PrintMethod extends EmptyVisitor implements MethodVisitor {

		@Override
		public void visitFieldInsn(int opcode, String owner, String name, String desc) {
			log.trace(("owner=" + owner + ", name=" + name + ", desc=" + desc));
			queue.offer(getClassName(owner));
		}

		@Override
		public void visitMethodInsn(int opcode, String owner, String name, String desc) {
			log.trace(("owner=" + owner + ", name=" + name + ", desc=" + desc));
			queue.offer(getClassName(owner));
		}

		private String getClassName(String owner) {
			return owner.replace('/', '.');
		}
	}
}
